<?php

/**
 * XmlToArray class file
 * 
 * Parser XML content to PHP array
 * @author Oanh Nguyen Ngoc <ngocoanh.bk52@gmail.com>
 * @package MyPlugin.Lib
 * @version 1.0
 */
class XmlToArray {

    const XML_PARSER_MODE_SIMPLE = 1;
    const XML_PARSER_MODE_ADVANCE = 2;

    /**
     * @var string
     * @access protected
     */
    protected $_xml = null;

    /**
     * @var int
     * @access protected
     */
    protected $_mode = 2;

    /**
     * @var string
     * @access protected
     */
    protected $_childrenKey = '_c';

    /**
     * @var string
     * @access protected
     */
    protected $_valueKey = '_v';

    /**
     * @var string
     * @access protected
     */
    protected $_attributesKey = '_a';

    /**
     * @param string $file Path to XML file
     * @param array $options
     */
    public function __construct($file, $options = array()) {
        if (is_file($file) && pathinfo($file, PATHINFO_EXTENSION) == 'xml') {
            $this->setXml(file_get_contents($file));
        } else {
            throw new Exception("\$file = '$file' is not xml file");
        }
        $this->setOptions($options);
    }

    public function setOptions($options = array()) {
        if (!is_array($options)) {
            return;
        }
        if (isset($options['mode'])) {
            $this->setMode($options['mode']);
        }
        if (isset($options['childrenKey'])) {
            $this->setChildrenKey($options['childrenKey']);
        }
        if (isset($options['valueKey'])) {
            $this->setValueKey($options['valueKey']);
        }
        if (isset($options['attributesKey'])) {
            $this->setAttributesKey($options['atributesKey']);
        }
    }

    public function setMode($mode) {
        //TODO :
        $this->_mode = $mode;
    }

    public function setXml($xml) {
        //TODO :
        $this->_xml = $xml;
    }

    public function setChildrenKey($key) {
        //TODO :
        $this->_childrenKey = $key;
    }

    public function setValueKey($key) {
        //TODO :
    }

    public function setAttributesKey($key) {
        //TODO :
    }

    public function getMode() {
        return $this->_mode;
    }

    public function getXml() {
        return $this->_xml;
    }

    public function getChildrenKey() {
        return $this->_childrenKey;
    }

    public function getValueKey() {
        return $this->_valueKey;
    }

    public function getAttributesKey() {
        return $this->_attributesKey;
    }

    /**
     * Parse content xml file to array 
     * 
     * @return array
     * @access protected
     */
    protected function _parser() {
        $parser = xml_parser_create();
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        xml_parse_into_struct($parser, $this->getXml(), $vals, $index);
        xml_parser_free($parser);

        $mnary = array();
        $ary = &$mnary;
        $_a = $this->getAttributesKey();
        $_c = $this->getChildrenKey();
        $_v = $this->getValueKey();
        foreach ($vals as $r) {
            $t = $r['tag'];
            if ($r['type'] == 'open') {
                if (isset($ary[$t])) {
                    if (isset($ary[$t][0]))
                        $ary[$t][] = array(); else
                        $ary[$t] = array($ary[$t], array());
                    $cv = &$ary[$t][count($ary[$t]) - 1];
                } else
                    $cv = &$ary[$t];
                if (isset($r['attributes'])) {
                    foreach ($r['attributes'] as $k => $v)
                        $cv[$_a][$k] = $v;
                }
                $cv[$_c] = array();
                $cv[$_c]['_p'] = &$ary;
                $ary = &$cv[$_c];
            } elseif ($r['type'] == 'complete') {
                if (isset($ary[$t])) { // same as open
                    if (isset($ary[$t][0]))
                        $ary[$t][] = array(); else
                        $ary[$t] = array($ary[$t], array());
                    $cv = &$ary[$t][count($ary[$t]) - 1];
                } else
                    $cv = &$ary[$t];
                if (isset($r['attributes'])) {
                    foreach ($r['attributes'] as $k => $v)
                        $cv[$_a][$k] = $v;
                }
                $cv[$_v] = (isset($r['value']) ? $r['value'] : '');
            } elseif ($r['type'] == 'close') {
                $ary = &$ary['_p'];
            }
        }

        $this->_del_p($mnary);
        return $mnary;
    }

    /**
     * Remove recursion in result array
     * 
     * @param array
     * @return void
     * @access protected
     */
    protected function _del_p(&$ary) {
        foreach ($ary as $k => $v) {
            if ($k === '_p')
                unset($ary[$k]);
            elseif (is_array($ary[$k]))
                $this->_del_p($ary[$k]);
        }
    }

    /**
     * Parse content xml file to array with simple mode
     * 
     * @param array $ary
     * @return array
     * @access protected
     */
    protected function _parserSimple($ary = array()) {
        $outAry = array();
        //TODO :
        return $outAry;
    }

    /**
     * Parse content xml file to array
     * 
     * @return array
     * @access public
     */
    public function toArray() {
        $ary = $this->_parser();
        if ($this->getMode() == self::XML_PARSER_MODE_SIMPLE) {
            $ary = $this->_parserSimple($ary);
        }
        return $ary;
    }

    /**
     * Parse xml file to array
     * 
     * @param string $file Path to XML file
     * @param array $options
     * @return array
     * @access public
     * @static
     */
    public static function xmlToArray($file, $options = array()) {
        $ob = new XmlToArray($file, $options);
        return $ob->toArray();
    }

}